#==============================================================================
# ** Sprite_Battle_GTBS
#------------------------------------------------------------------------------
#  This sprite is used to display characters. It observes a instance of the
# Game_Actor/Enemy class and automatically changes sprite conditions.
#==============================================================================

class Sprite_Battler_GTBS < Turn_Sprite
  #--------------------------------------------------------------------------
  # * Constants
  #--------------------------------------------------------------------------
  WHITEN    = 1                      # Flash white (start action)
  BLINK     = 2                      # Blink (damage)
  APPEAR    = 3                      # Appear (appear, revive)
  DISAPPEAR = 4                      # Disappear (escape)
  COLLAPSE  = 5                      # Collapse (incapacitated)
  BALLOON_WAIT = 12                  # Final balloon frame wait time
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :battler
  attr_reader   :_damage_duration
  attr_reader   :_animation_duration 
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     viewport  : viewport
  #     battler   : character (Game_Battler)
  #--------------------------------------------------------------------------
  def initialize(viewport, battler = nil)
    super(viewport)
    @battler = battler
    @balloon_duration = 0
    @effect_duration = 0
    @battler_visible = false
    @down            = false
    @pose_started    = false
    @anim            = false
    @last_time, @frame_index, @pose = 0, 0, 0
    @loop_type = GTBS::DEFAULT_POSE_NUMBERS['Wait'] + 
      GTBS::DEFAULT_POSE_NUMBERS['Walk'] + GTBS::DEFAULT_POSE_NUMBERS['Defend'] + 
      GTBS::DEFAULT_POSE_NUMBERS['Dead'] + GTBS::DEFAULT_POSE_NUMBERS['Near Death']
  end
  #----------------------------------------------------------------------------
  # Bat - this method returns the battler that is held in this object
  #----------------------------------------------------------------------------
  def bat
    return @battler
  end
  #----------------------------------------------------------------------------
  # Dispose Process
  #----------------------------------------------------------------------------
  def dispose
    dispose_bitmap
    dispose_balloon
    super
  end
  #----------------------------------------------------------------------------
  # Dispose Bitmap - erases image
  #----------------------------------------------------------------------------
  def dispose_bitmap
    if self.bitmap != nil
      self.bitmap.dispose
    end
  end
  #----------------------------------------------------------------------------
  # * Update
  #----------------------------------------------------------------------------
  def update
    super
    if @battler == nil and !GTBS::VX
      self.bitmap = nil
      loop_animation(nil)
      return
    end
    update_bitmap
    update_state_animation if !GTBS::VX
    update_opacity
    update_blink
    update_check_hidden
    if @battler_visible
      if @battler.hidden and !GTBS::VX
        Sound.play_escape
        escape
        @battler_visible = false
      end
      update_white_flash
      update_state_pose
      check_animations
      check_damage_pop
      check_collapse
    end
    update_pose
    self.x =  @battler.screen_x
    self.y = @battler.screen_y
    self.z = @battler.screen_z(@ch)
    offset_large_unit
    self.blend_type = @battler.blend_type
    self.bush_depth = @battler.bush_depth
    if GTBS::VX
      update_balloon
      if @battler.balloon_id != 0
        @balloon_id = @battler.balloon_id
        start_balloon
        @battler.balloon_id = 0
      end
      setup_new_effect
      update_effect
    end
  end
  #----------------------------------------------------------------------------
  # Offset Large Unit - Sprite
  #----------------------------------------------------------------------------
  def offset_large_unit
    #large unit position update
    if !$game_map.iso?
      self.x += 16*(@battler.unit_size-1)
      self.y += 16*(@battler.unit_size-1)
    else
      self.y += 16*(@battler.unit_size-1)
    end
  end
  #----------------------------------------------------------------------------
  # Update Bitmap - Updates the battler info and sprite info
  #----------------------------------------------------------------------------
  def update_bitmap
    if @tile_id != @battler.tile_id or
       @character_name != @battler.character_name or
       @character_hue != @battler.character_hue
      @tile_id = @battler.tile_id
      @character_name = @battler.character_name
      @character_hue = @battler.character_hue
      if @tile_id >= 384
        self.bitmap = RPG::Cache.tile($game_map.tileset_name,
          @tile_id, @battler.character_hue)
        self.src_rect.set(0, 0, 32, 32)
        self.ox = 16
        self.oy = 32
      else
        self.bitmap = RPG::Cache.character(@battler.character_name,
          @battler.character_hue)
        set_dimensions
        self.ox = @cw / 2
        self.oy = @ch
      end
    end 
  end
  #----------------------------------------------------------------------------
  # Set Dimensions - Sets the cut width and cut height to be used when setting the
  # image. 
  #----------------------------------------------------------------------------
  def set_dimensions
    if !@character_name.include?("ANIM")
      @anim = true
      @cw = bitmap.width / 4
      @ch = bitmap.height / 4
      @index_array = [0,1,2,3]
    else
      w,h = check_frame_pose_overrrides
      @cw = bitmap.width / w
      @ch = ((bitmap.height / h) / 4)
      @index_array = nil
    end
  end
  #----------------------------------------------------------------------------
  #Check Frame/Pose Overrides
  #----------------------------------------------------------------------------
  # Returns the number of frames and poses for the current battler
  #----------------------------------------------------------------------------
  def check_frame_pose_overrrides
    # Fetch default stances and frame counts
    frames  = GTBS::DEFAULT_POSE_FRAMES
    stances = GTBS::DEFAULT_POSE_STANCES
    
    # Process to ensure that overrides do not exist
    if @battler.is_a?(Game_Actor)
      # * Stances
      #------------------------------------------------------------
      stance_hash = GTBS::EXTRA_ACTOR_STANCES[@battler.id]
      if stance_hash != nil
        stances = stance_hash
      end
      # * Frames
      #------------------------------------------------------------
      frame_hash = GTBS::EXTRA_ACTOR_FRAMES[@battler.id]
      if frame_hash != nil
        for key in frame_hash.keys
          if frame_hash[key] > frames
            frames = frame_hash[key]
          end
        end
        # If a frame hash exist for all of the stances, ensure that total frames is
        # not less than default.  If so, set to be lower than default.  
        if (frame_hash.keys.size == stances) and frames == GTBS::DEFAULT_POSE_FRAMES
          frames = 0
          for key in frame_hash.keys
            frames = frame_hash[key]
          end
        end
      end
    else
      # * Stances
      #------------------------------------------------------------
      stance_hash = GTBS::EXTRA_ENEMY_STANCES[@battler.enemy_id]
      if stance_hash != nil
        stances = stance_hash
      end
      # * Frames
      #------------------------------------------------------------
      frame_hash = GTBS::EXTRA_ENEMY_FRAMES[@battler.enemy_id]
      if frame_hash != nil
        for key in frame_hash.keys
          if frame_hash[key] > frames
            frames = frame_hash[key]
          end
        end
        # If a frame hash exist for all of the stances, ensure that total frames is
        # not less than default.  If so, set to be lower than default.  
        if (frame_hash.keys.size == stances) and frames == GTBS::DEFAULT_POSE_FRAMES
          frames = 0
          for key in frame_hash.keys
            frames = frame_hash[key]
          end
        end
      end
    end
    return frames, stances
  end
  #----------------------------------------------------------------------------
  # Updates STATE animations
  #----------------------------------------------------------------------------
  def update_state_animation
    if @battler.damage == nil and @battler.state_animation_id != @state_animation_id
      @state_animation_id = @battler.state_animation_id
      loop_animation($data_animations[@state_animation_id])
    end
  end
  #----------------------------------------------------------------------------
  # Updates the opacity of the sprite
  #----------------------------------------------------------------------------
  def update_opacity
    self.visible = (not @battler.transparent)

    if @battler_visible 
      if $game_system.acted.include?(@battler)
        self.opacity = GTBS::DIM_OPACITY
      else
        self.opacity = 235
      end
    end
  end
  #----------------------------------------------------------------------------
  # Updates the Blink (white flash) of the sprite
  #----------------------------------------------------------------------------
  def update_blink
    if @battler.blink
      blink_on
    else
      blink_off
    end
  end
  #----------------------------------------------------------------------------
  # Updates hidden status
  #----------------------------------------------------------------------------
  def update_check_hidden
    unless @battler_visible
      if not @battler.hidden and not @battler.dead? and
         (@battler.damage == nil or @battler.damage_pop)
        appear
        @down = false
        @battler_visible = true
      end
    end
  end
  #----------------------------------------------------------------------------
  # Updates the white flash process
  #----------------------------------------------------------------------------
  def update_white_flash
    if @battler.white_flash
      whiten
      @battler.white_flash = false
    end
  end
  #----------------------------------------------------------------------------
  # Update pose information based on current STATE animation ID
  #----------------------------------------------------------------------------
  def update_state_pose
    if @battler.state_animation_id != 0
      if GTBS::ANIM_BATTLER
        if @battler.state_animation_id == GTBS::ANIM_CASTING
          @battler.set_pose("casting")
        end
      end
      if @battler.state_animation_id == GTBS::DOOM_ANIM_ID
        if @battler.doom_counter != nil
          cx = self.bitmap.text_size(@battler.doom_counter.to_s).width
          x = (self.bitmap.width/2)-(cx/2)
          self.bitmap.draw_text(x,self.bitmap.height-24,cx, 32,@battler.doom_counter.to_s)
        end
      end
    end
  end
  #-------------------------------------------------------------
  # Checks to see if the animation_id for the battler happens to be a custom defined
  # trigger for the current battler
  #-------------------------------------------------------------
  def check_custom_assign_ids
    if @battler.is_a?(Game_Actor)
      anim_hash = GTBS::CUST_SKILL_POSE_ASSIGN_ACTOR[@battler.id]
    else
      anim_hash = GTBS::CUST_SKILL_POSE_ASSIGN_ENEMY[@battler.enemy_id]
    end
    if anim_hash != nil and anim_hash.keys.include?(@battler.animation_id)
      @battler.set_pose((anim_hash[@battler.animation_id]-1))
      @battler.animation_id = 0
    end
  end
  #----------------------------------------------------------------------------
  # Apply pose animation intercept if any
  #----------------------------------------------------------------------------
  def check_animations
    if @battler.animation_id != 0
      if GTBS::ANIM_BATTLER
        check_custom_assign_ids
        case @battler.animation_id
        when GTBS::ANIM_ATK
          @battler.set_pose("attack")
          @battler.animation_id = 0
          @test = true
        when GTBS::ANIM_SPEC1
          @battler.set_pose("spec1")
          @battler.animation_id = 0
        when GTBS::ANIM_CASTING
          @battler.set_pose("casting")
          @battler.animation_id = 0
        when GTBS::ANIM_CAST
          @battler.set_pose("cast")
          @battler.animation_id = 0
        when GTBS::ANIM_HEAL
          @battler.set_pose("heal")
          @battler.animation_id = 0
        else
          animation = $data_animations[@battler.animation_id]
          if @battler.attacker_dir > 0
            animation(animation, @battler.animation_hit, @battler.attacker_dir)
            @battler.attacker_dir = -1
          else
            animation(animation, @battler.animation_hit)
          end
          
          @battler.animation_id = 0
        end
      else
        animation = $data_animations[@battler.animation_id]
        if @battler.attacker_dir > 0
          animation(animation, @battler.animation_hit, @battler.attacker_dir)
          @battler.attacker_dir = -1
        else
          animation(animation, @battler.animation_hit)
        end
        @battler.animation_id = 0
      end
    end
  end
  #----------------------------------------------------------------------------
  # Processes damage pop for doom and updates pose based on damage values
  #----------------------------------------------------------------------------
  def check_damage_pop
    if @battler.damage2_pop
      doom_pop(@battler.damage2)
      @battler.damage2 = nil
      @battler.damage2_pop = false
    end
    if @battler.damage_pop
      damage(@battler.damage, @battler.critical)
      if @battler.damage.is_a?(Numeric)
        if @battler.damage < 0
          @battler.set_pose("heal") unless !GTBS::HEAL_POSE
        else
          @battler.set_pose("pain") 
        end
      else
        if @battler.damage == "Miss" or @battler.damage == "Dodge"
          @battler.set_pose("defend")
        elsif @battler.damage.include?("CRITICAL") or @battler.damage.include?('DOOM!')
          @battler.set_pose("pain") 
        else  
          @battler.set_pose("heal") unless !GTBS::HEAL_POSE
        end
      end
      @battler.damage = nil
      @battler.critical = false
      @battler.damage_pop = false
    end
  end
  #----------------------------------------------------------------------------
  # Check if the battler has died
  #----------------------------------------------------------------------------
  def check_collapse
    override = (!@character_name.include?("ANIM") and @battler.dead? and !@battler.collapsed?)
    if ((@battler.damage == nil and @battler.dead? and !@battler.collapsed?) or 
      override) and !self.effect?
      
      if @battler.is_a?(Game_Actor) and GTBS::CHECK_DEATH_ANIMATION_ACTOR[@battler.id] != nil
        @battler.animation_id = GTBS::CHECK_DEATH_ANIMATION_ACTOR[@battler.id]
        check_animations
      elsif @battler.is_a?(Game_Enemy) and GTBS::CHECK_DEATH_ANIMATION_ENEMY[@battler.enemy_id] != nil
        @battler.animation_id = GTBS::CHECK_DEATH_ANIMATION_ENEMY[@battler.enemy_id]
        check_animations
      end  
      pose_hash = GTBS::DEFAULT_POSE_NUMBERS
      #Ensure that current pose is not pain or death
      if !(pose_hash["Pain"]+pose_hash["Dead"]).include?(@battler.pose?+1) or override
        if @battler.is_a?(Game_Actor)
          sum_override = GTBS::SUMMON_CLASS.include?(@battler.class_id)
        else
          sum_override = GTBS::SUMMON_ENID.include?(@battler.enemy_id)
        end
        if GTBS::REMOVE_DEAD == 2 or sum_override
          if @battler.is_a?(Game_Enemy)
            $game_system.se_play($data_system.enemy_collapse_se)
          else
            $game_system.se_play($data_system.actor_collapse_se)
          end
          collapse
          #@battler.collapsed = true
          @battler_visible = false
        elsif GTBS::REMOVE_DEAD == 1
          if @battler.is_a?(Game_Enemy)
            $game_system.se_play($data_system.enemy_collapse_se)
            collapse
            #@battler.collapsed = true
            @battler_visible = false
          else
            $game_system.se_play($data_system.actor_collapse_se)
            @battler.collapsed = true
            @battler.set_pose("collapse")
          end
        else
          @battler.collapsed = true
          @battler.set_pose("collapse")
          if @battler.is_a?(Game_Enemy)
            $game_system.se_play($data_system.enemy_collapse_se)
          else
            $game_system.se_play($data_system.actor_collapse_se)
          end
        end
      end
    end
  end
  #------------------------------------------------------------- 
  # Effect? - Is used by battle system to determine if ready to proceed
  #-------------------------------------------------------------
  def effect?
    result = super
    return result if result
    if @anim and GTBS::ANIM_BATTLER
      if @loop_type.include?(@pose+1)
        return false
      else
        return true
      end
    end
  end
  #----------------------------------------------------------------------------
  # Update the current actor "pose" frame
  #----------------------------------------------------------------------------
  def update_pose
    if @tile_id == 0
      #--------------------------------------------------------------
      # If not animated battler
      #--------------------------------------------------------------
      if !@character_name.include?("ANIM")
        #get sx
        if (@battler.dead? and @battler.collapsed) or (@character_name.include?("down"))
          sx = @battler.down_x * @cw
        else 
          sx = @battler.pattern * @cw
        end
        #get sy
        if @battler.dead? and @battler.collapsed
          sy = @battler.down_y * @ch
        else 
          sy = (@battler.direction - 2) / 2 * @ch
        end
      #--------------------------------------------------------------
      #If Animated Battler
      #--------------------------------------------------------------
      else
        # Set defend pose if current_actions is defend type
        if @battler.pose? == GTBS::DEFAULT_POSE_NUMBERS["Wait"] and 
          @battler.current_action.basic == 1
          
          @battler.set_pose("defend")
        end
        nd_val = get_near_death_value
        # If waiting or defending, but near death, set near death pose
        if (GTBS::DEFAULT_POSE_NUMBERS["Wait"]+ GTBS::DEFAULT_POSE_NUMBERS["Defend"]).include?(@battler.pose?+1) and
          nd_val > (@battler.hp.to_f/@battler.maxhp)
          @battler.set_pose("near_death")
        end
        # Update pose variable
        if @pose != @battler.pose?
          @pose = @battler.pose?
          @index_array = nil
          @pose_started = false
          @frame_index = 0
        end
        #--------------------------------------------------------------
        # Get frame index
        #--------------------------------------------------------------
        if @index_array.nil?
          update_frame_index_array
        end
        
        if !@pose_started
          update_sound_association
          @pose_started = true
        end
        
        update_frame_index
        multiplier = @pose * 4 #direction multiplier
        cur_frame = @index_array[@frame_index] #gets from number from index array
        sx = cur_frame * @cw #sets start x for cut window
        sy = (((@battler.direction - 2) / 2) * @ch) + (multiplier*@ch)
      end
      #Set crop rect
      self.src_rect.set(sx, sy, @cw, @ch)
    end
  end
  #------------------------------------------------------------- 
  # Get Near Death Value - Returns the battler near death percentage
  #-------------------------------------------------------------
  def get_near_death_value
    # Get Near Death values
    if @battler.is_a?(Game_Actor)
      override = GTBS::ACTOR_ND_PERC_OVERRIDE[@battler.id]
      nd_val = override.nil? ? GTBS::DEFAULT_ND_PERCENTAGE/100.0 : override/100.0
    else
      override = GTBS::ENEMY_ND_PERC_OVERRIDE[@battler.enemy_id]
      nd_val = override.nil? ? GTBS::DEFAULT_ND_PERCENTAGE/100.0 : override/100.0
    end
    return nd_val
  end
  #-------------------------------------------------------------
  # Update Battler Pose
  #-------------------------------------------------------------
  # Resets pose to wait if applicable
  #-------------------------------------------------------------
  def update_battler_pose
    if GTBS::DEFAULT_POSE_NUMBERS["Walk"].include?(@pose+1) 
      @battler.set_pose('wait') unless @battler.moving?
    elsif !@loop_type.include?(@pose+1)
      @battler.set_pose('wait')
    elsif GTBS::DEFAULT_POSE_NUMBERS["Dead"].include?(@pose+1) 
      if !@battler.dead?
        @battler.set_pose('wait')
        @battler.collapsed = false
      end
    elsif GTBS::DEFAULT_POSE_NUMBERS["Near Death"].include?(@pose+1) and 
      get_near_death_value <= (@battler.hp.to_f/@battler.maxhp)
      
      @battler.set_pose('wait')
    end
  end
  #-------------------------------------------------------------
  # Update Frame Index
  #-------------------------------------------------------------
  # Current Frame Management
  #-------------------------------------------------------------
  def update_frame_index
    speed = GTBS::DEFAULT_FRAME_SPEED
    if @battler.is_a?(Game_Actor)
      pose_rate = GTBS::POSE_FRAME_OVERRIDE_ACTOR[@battler.id]
      if pose_rate != nil and pose_rate.keys.include?(@pose)
        speed = pose_rate[@pose]
      end
    else
      pose_rate = GTBS::POSE_FRAME_OVERRIDE_ENEMY[@battler.enemy_id]
      if pose_rate != nil and pose_rate.keys.include?(@pose)
        speed = pose_rate[@pose]
      end
    end
    speed = adjust_for_states(speed)
    #update the frame
    time = Graphics.frame_count / (Graphics.frame_rate / speed)
    if @last_time < time
      @frame_index = (@frame_index + 1) % @max_frame
      if @frame_index == 0
        update_battler_pose
      end
    end
    @last_time = time
  end
  #-------------------------------------------------------------
  # Adjust for States updates the framerate speed to reflect special states, such 
  # as HASTE, SLOW, or STOP
  #-------------------------------------------------------------
  def adjust_for_states(speed)
    if @battler.state?(GTBS::HASTE_ID)
      speed *= 1.2
    elsif @battler.state?(GTBS::SLOW_ID)
      speed *= 0.8
    end
    return speed.to_i
  end
  #-------------------------------------------------------------
  # Checks for a sound association for specified pose
  #-------------------------------------------------------------
  def update_sound_association
    if @battler.is_a?(Game_Actor)
      hash = GTBS::STANCE_SOUND_ASSOCIATION_ACTOR[@battler.id]
      if hash != nil and hash.keys.include?(@pose+1)
        @battler.animation_id = hash[@pose+1]
      end
    else
      hash = GTBS::STANCE_SOUND_ASSOCIATION_ENEMY[@battler.enemy_id]
      if hash != nil and hash.keys.include?(@pose+1)
        @battler.animation_id = hash[@pose+1]
      end
    end
  end
  #-------------------------------------------------------------
  # Update Frame Index Array
  #-------------------------------------------------------------
  # Sets the index advance array.  This is used to determine which order the frames
  # should be played.
  #-------------------------------------------------------------
  def update_frame_index_array
    if @battler.is_a?(Game_Actor)
      hash = GTBS::ACTOR_POSE_PATH[@battler.id]
      if hash != nil and hash.keys.include?(@pose+1)
        @index_array = hash[@pose+1]
        @max_frame = hash[@pose+1].size
      else
        hash = GTBS::EXTRA_ACTOR_FRAMES[@battler.id]
        if hash != nil and hash.keys.include?(@pose+1)
          @max_frame = hash[@pose+1]
        else
          @max_frame = GTBS::DEFAULT_POSE_FRAMES
        end
        @index_array = []
        for i in 0...@max_frame
          @index_array.push(i)
        end
      end
    else
      hash = GTBS::ENEMY_POSE_PATH[@battler.enemy_id]
      if hash != nil and hash.keys.include?(@pose+1)
        @index_array = hash[@pose+1]
        @max_frame = hash[@pose+1].size
      else
        hash = GTBS::EXTRA_ENEMY_FRAMES[@battler.enemy_id]
        if hash != nil and hash.keys.include?(@pose+1)
          @max_frame = hash[@pose+1]
        else
          @max_frame = GTBS::DEFAULT_POSE_FRAMES
        end
        @index_array = []
        for i in 0...@max_frame
          @index_array.push(i)
        end
      end
    end
  end
end

    